Ext.namespace("Wdg.calendar");

// TODO: add localization support (ex: CanadianLongDate: "...")
Wdg.calendar.DatePatterns = 
{
    ISO8601Long: "Y-m-d H:i:s",
    ISO8601Short: "Y-m-d",
    ShortDate: "n/j/Y",
    LongDate: "l, F d, Y",
    FullDateTime: "l, F d, Y g:i:s A",
    MonthDay: "F d",
    ShortTime: "g:i A",
    LongTime: "g:i:s A",
    SortableDateTime: "Y-m-d\\TH:i:s",
    UniversalSortableDateTime: "Y-m-d H:i:sO",
    YearMonth: "F, Y"
};

Wdg.calendar.CalendarMode = 
{
    Day: 0,
    Week: 1,
    Month: 2
};

Wdg.calendar.CalendarModeLookup = function (val)
{
    var calendarMode = Wdg.calendar.CalendarMode;

    for (var mode in calendarMode)
    {
        if (calendarMode[mode] === val)
            return mode;
    }

    return null;
};

Wdg.calendar.Calendar = Ext.extend(Ext.Container,
{
    cls: "Calendar",	
    
    initComponent : function ()
    {
    	Wdg.calendar.Calendar.superclass.initComponent.apply(this, arguments);

        this.on("afterlayout", this.onAfterLayout, this);

        Wdg.calendar.CalendarContainerManager.registerMember("Calendar", this);        
    },
    
    onRender : function ()
    {
    	Wdg.calendar.Calendar.superclass.onRender.apply(this, arguments);
    	
    	this.getEl().setVisible(false); // set initial visibility to false, until the initial layout is complete
    	
    	this.createView(this.mode);
    },
    
    createTitle : function (viewId)
    { 
    	this.todaysDate = new Date();
        
        //initially hide the title until it is finished laying out
        Ext.DomHelper.append(viewId,
        {
            id: "CalendarTitle", 
            tag: "div",
            cls: "CalendarTitle",
            style: "visibility: hidden;",
            html: this.todaysDate.format(Wdg.calendar.DatePatterns.LongDate)
        });   
        
        this.calendarTitleEl = Ext.get("CalendarTitle");
    },

    createDayView : function ()
    {
        this.createTitle(this.viewId);

        var cfg =
        {
            viewId: this.viewId,
            mode: this.mode,
            ownerCt: this
        };

        this.timeRuler = new Wdg.calendar.TimeRuler(cfg);
        this.dataScroller = new Wdg.calendar.DataScroller(cfg);
    },

    createView : function (mode)
    {	
    	var viewId = this.viewId = Wdg.calendar.CalendarModeLookup(mode) + "View";
    	
        Ext.DomHelper.append(this.getEl(), {
            id: viewId, 
            tag: "div"
        });
    	
    	if (mode === Wdg.calendar.CalendarMode.Day)
            this.createDayView();
    },
    
    onAfterLayout : function ()
    {  
        this.timeRuler.render(this.viewId);
        this.dataScroller.render(this.viewId);
        
        Wdg.calendar.CalendarContainerManager.layoutContainers(this.mode);
        Wdg.calendar.CalendarContainerManager.layoutTitle(this.mode);
        
        var animate = false;
        if (this.animateOnInitialLoad && !this.finishedAnimateOnInitialLoad)
        {
            this.finishedAnimateOnInitialLoad = true;
            animate = true;
        }
        
        this.getEl().setVisible(true, animate);
    }    
});

// singleton
// Manage the communication between containers
Wdg.calendar.CalendarContainerManager = function ()
{
    var members = {};
    
    function fillTimeRulerToView (timeRulerContainerEl, timeRulerViewEl, dayViewEl, timeBlockHeight)
    {
        var calendarContainer = members.Calendar;
        
        for (var i = 0; i < 24; i++)
        {      
            var timeEl = Ext.get(Wdg.calendar.DayTimes[i]); 
            
            Ext.get(Wdg.calendar.DayTimes[i]).dom.style.height = timeBlockHeight - 
                        (timeEl.getPadding("tb") + timeEl.getBorderWidth("tb")) + "px";            
        }
        
        dayViewEl.setHeight(timeRulerViewEl.dom.offsetHeight);
    }
    
    function fixupTimeRuler ()
    {
        var calendarContainer = members.Calendar;
        
        for (var i = 0; i < 24; i++)
        {
            var timeEl = Ext.get(Wdg.calendar.DayTimes[i]);            
            var timeY = timeEl.getY();
            var timeRuler = calendarContainer.timeRuler;
            
            if (timeY - timeRuler.getEl().getY() === 2)
            {
                var offsetPrevTimeEl = Ext.get(Wdg.calendar.DayTimes[i - 1]);
                timeRuler.offsetPrevTimeEl = offsetPrevTimeEl;
                
                if (offsetPrevTimeEl.getBorderWidth("b") > 0)
                    timeRuler.offsetPrevTimeElBorderWidth = offsetPrevTimeEl.getBorderWidth("b");
                
                timeRuler.offsetPrevTimeElPadding = offsetPrevTimeEl.getPadding("b");
                                
                offsetPrevTimeEl.set(
                {
                    style: "border-bottom-width: 0px;"
                });
            }
        }
    }
    
    function isLayoutStructured (mode)
    {
        if (mode === Wdg.calendar.CalendarMode.Day)
        {
            var calendarContainer = members.Calendar;
            if (!calendarContainer)
                throw "Component layout failed. A Calendar is required for proper layout of the component.";            
            
            var calendarContainerEl = calendarContainer.getEl();
            var calendarTitleEl = calendarContainer.calendarTitleEl;
            var DataScroller = calendarContainer.dataScroller;
            var DataScrollerContainerEl = DataScroller.getEl();
            var timeRuler = calendarContainer.timeRuler;
            var timeRulerContainerEl = calendarContainer.timeRuler.getEl();
            
            if (!calendarContainerEl)
                throw "Component layout failed. A Calendar must have a valid Ext.Element associated with it.";
            else if (!calendarTitleEl)
                throw "Component layout failed. A Calendar must have a valid CalendarTitle Ext.Element associated with it."; 
            else if (!DataScrollerContainerEl)
                throw "Component layout failed. A DataScroller must have a valid Ext.Element associated with it."; 
            else if (!timeRulerContainerEl)
                throw "Component layout failed. A TimeRuler must have a valid Ext.Element associated with it.";
           
            var dayViewEl = DataScroller.dayViewEl;
            var timeRulerViewEl = timeRuler.timeRulerViewEl;
           
            if (!dayViewEl)
                throw "Component layout failed. A DayViewContainer must have a valid Ext.Element associated with it.";
            else if (!timeRulerViewEl)
                throw "Component layout failed. A TimeRulerViewContainer must have a valid TimeRulerView Ext.Element associated with it.";
        }
        
        return true;
    }
    
    return {
        registerMember : function (id, container)
        {
            members[id] = container;
        },
        
        layoutContainers : function (mode)
        {  
            if (isLayoutStructured(mode))
            {
                if (mode === Wdg.calendar.CalendarMode.Day)
                {	
                    var calendarContainer = members.Calendar;
                    
                    var calendarContainerEl = calendarContainer.getEl();
                    var calendarTitleEl = calendarContainer.calendarTitleEl;
                    var DataScroller = calendarContainer.dataScroller;
                    var DataScrollerContainerEl = DataScroller.getEl();
                    var timeRuler = calendarContainer.timeRuler;
                    var timeRulerContainerEl = calendarContainer.timeRuler.getEl();
                    
                    // TODO: programatically get the View based off of the mode
                    var dayViewEl = DataScroller.dayViewEl;
                    var timeRulerViewEl = timeRuler.timeRulerViewEl;
                    
                    timeRulerContainerEl.setHeight(calendarContainerEl.dom.offsetHeight - calendarTitleEl.dom.offsetHeight);
                    DataScrollerContainerEl.setHeight(calendarContainerEl.dom.offsetHeight - calendarTitleEl.dom.offsetHeight);
                    
                    var timeBlockHeight = (timeRulerContainerEl.dom.offsetHeight / 
                            Math.floor(((calendarContainer.endMinute / 60) - (calendarContainer.startMinute / 60)) + 1)); 
                    
                    fillTimeRulerToView(timeRulerContainerEl, timeRulerViewEl, dayViewEl, timeBlockHeight);
                    
                    this.scrollContainers(calendarContainer, timeRulerContainerEl, DataScrollerContainerEl, timeBlockHeight);
                    
                    fixupTimeRuler();                    
                }
            }      
        },
        
        layoutTitle : function (mode)
        {
            if (isLayoutStructured(mode))
            {   
                var calendarContainer = members.Calendar;
                
                var size = calendarContainer.getSize();
                var width;
                
                var calendarContainerEl = calendarContainer.getEl();
                var calendarTitleEl = calendarContainer.calendarTitleEl;
                var DataScrollerContainerEl = calendarContainer.dataScroller.getEl();                
                
                if ((calendarContainerEl.dom.offsetHeight - calendarTitleEl.dom.offsetHeight) < 
                        (DataScrollerContainerEl.dom.scrollHeight))
                    width = size.width - calendarTitleEl.dom.offsetLeft - 
                                Wdg.calendar.getScrollBarWidth(true);
                else
                    width = size.width - calendarTitleEl.dom.offsetLeft;
            
                calendarTitleEl.setWidth(width);
                calendarTitleEl.setVisible(true);
            }
        },
        
        scrollAssociatedContainer : function (mode, currentScrollTop)
        {
            if (isLayoutStructured(mode))
            {
                if (mode === Wdg.calendar.CalendarMode.Day)
                {
                    var calendarContainer = members.Calendar;
                    
                    var timeRuler = calendarContainer.timeRuler;
                    var timeRulerContainerEl = calendarContainer.timeRuler.getEl();
                    var DataScroller = calendarContainer.dataScroller;
                    
                    timeRulerContainerEl.dom.scrollTop = currentScrollTop;             
                    
                    if (DataScroller.isUserScroll !== false)
                        DataScroller.userHasScrolled = true;
                    
                    if (timeRuler.offsetPrevTimeEl && DataScroller.isUserScroll !== false)
                    { 
                        timeRuler.offsetPrevTimeEl.set(
                        {
                            style: "border-bottom-width: " + timeRuler.offsetPrevTimeElBorderWidth + "px;"
                        });                
                    }
                    
                    delete DataScroller.isUserScroll;
                }
            }
        },
        
        scrollContainers : function (calendarContainer, timeRulerContainerEl, DataScrollerContainerEl, timeBlockHeight)
        {
            var DataScroller = calendarContainer.dataScroller;
            DataScroller.isUserScroll = false;
            
            DataScroller.prevScrollTop = DataScrollerContainerEl.dom.scrollTop;
            timeRulerContainerEl.prevScrollTop = timeRulerContainerEl.dom.scrollTop;
            
            var scrollTop;
            var startHourUnits;
            if (DataScroller.userHasScrolled)
            {
                console.info("prevScrollTop" + DataScroller.prevScrollTop); 
                console.info("prevTimeBlockHeight" + DataScroller.prevTimeBlockHeight);
                                
                startHourUnits = DataScroller.prevScrollTop / DataScroller.prevTimeBlockHeight;
                
                scrollTop = startHourUnits * timeBlockHeight;
            }
            else
            {
                startHourUnits = Math.floor(calendarContainer.startMinute / 60);
                scrollTop = startHourUnits * timeBlockHeight;
            }
            
            DataScroller.prevTimeBlockHeight = timeBlockHeight;
            
            timeRulerContainerEl.dom.scrollTop = scrollTop;
            DataScrollerContainerEl.dom.scrollTop = scrollTop;
        },
        
        unRegisterMember : function (id)
        {
            delete members[id];
        }
    };
}();

Wdg.calendar.DataScroller = Ext.extend(Ext.Container,
{
    //const
    internalId: "DataScroller",
        
    initComponent : function ()
    {
    	Wdg.calendar.DataScroller.superclass.initComponent.apply(this, arguments);     
        
        this.el = Ext.DomHelper.append(this.viewId,
        {
            id: this.internalId,
            tag: "div",
            cls: "DataScroller"
        });

        this.createContent();
    },
    
    // TODO: programatically get the View based off of the mode
    createContent : function ()
    {
        this.contentId = Wdg.calendar.CalendarModeLookup(this.mode) + "Content";
        
        Ext.DomHelper.append(this.internalId,
        {
            id: this.contentId,
            tag: "div",
            cls: "DayView",
            html: "Meeting 1"
        });
        this.dayViewEl = Ext.get(this.contentId);
    },

    afterRender : function ()
    {
        Wdg.calendar.DataScroller.superclass.afterRender.apply(this, arguments);
         
        this.getEl().on("scroll", function (ev, targetEl, obj)
        {
            Wdg.calendar.CalendarContainerManager.scrollAssociatedContainer(Wdg.calendar.CalendarMode.Day, targetEl.scrollTop);
        }, this);
    }
});

Wdg.calendar.TimeRuler = Ext.extend(Ext.Container,
{
    //const
    internalId: "TimeRuler",
    
    initComponent : function ()
    {
    	Wdg.calendar.TimeRuler.superclass.initComponent.apply(this, arguments);        
        
        this.el = Ext.DomHelper.append(this.viewId, {
            id: this.internalId, 
            tag: "div", 
            cls: "TimeRuler"
        });

        this.createContent();       
    },

    createContent : function ()
    {
        this.contentId = "TimeRuler" + Wdg.calendar.CalendarModeLookup(this.mode) + "Content";
        
        Ext.DomHelper.append(this.internalId,
        {
            id: this.contentId,
            tag: "div"
        });
        this.timeRulerViewEl = Ext.get(this.contentId);

        for (var i = 0; i < 24; i++)
        {
            var cls = "DayViewTimeColumn DayViewTimeBorder";

            if (i === 23)
                cls += " DayViewTimeColumnBorderNoBottom";
            else
                cls += " DayViewTimeColumnBorderBottom";

            Ext.DomHelper.append(this.contentId, {
                id: Wdg.calendar.DayTimes[i],
                tag: "div",
                cls: cls,
                html: Wdg.calendar.DayTimes[i]
            });
        }
    }
});

Wdg.calendar.DayTimes = function ()
{
    var hour = 12;
    var ampm = "AM";
    var dayTimes = [];
    
    for (var i = 0; i < 24; i++)
    {   
        if (i > 0 && (hour > 11))
        {
            hour = 12;
            ampm = "PM";
        }
        
        var dayStr = hour + ":00 " + ampm;
        dayTimes.push(dayStr);
        
        hour++;
        
        if (hour === 13)
            hour = 1;
    }
    
    return dayTimes;
}();

Wdg.calendar.Meeting = Ext.extend(Ext.Panel,
{
    collapsible: true,
    
    draggable: 
    {
        insertProxy: false,
        
        onDrag : function (x, y)
        {
            var proxy = this.proxy.getEl();
            
            this.x = (proxy.getLeft() - 9);
            this.y = (proxy.getTop() - 34);
        },
        
        endDrag : function (ev)
        {
            this.panel.setPosition(this.x, this.y);
        }
    },
    
    frame: true,
    
    height: 200,
    
    width: 200,
    
    constructor : function (cfg)
    {
        Ext.apply(this, cfg);
        Wdg.Meeting.superclass.constructor.call(this, cfg);
    }    
});

Wdg.calendar.getScrollBarWidth = function ()
{
    return Ext.getScrollBarWidth(true) - 2;
};

Ext.reg("calendar", Wdg.calendar.Calendar);